"""
日期处理工具

提供日期相关的实用函数。
"""

from datetime import date, datetime, timedelta
from typing import List, Tuple, Optional
import calendar
from dateutil.relativedelta import relativedelta


class DateUtils:
    """日期工具类"""

    @staticmethod
    def get_month_range(start_date: date, end_date: date) -> List[Tuple[date, date]]:
        """
        获取指定日期范围内的月份范围列表

        Args:
            start_date: 开始日期
            end_date: 结束日期

        Returns:
            月份范围列表，每个元素为(月初日期, 月末日期)
        """
        months = []
        current = start_date.replace(day=1)  # 月初

        while current <= end_date:
            # 计算月末
            next_month = current + relativedelta(months=1)
            month_end = next_month - timedelta(days=1)

            # 调整为实际范围
            period_start = max(current, start_date)
            period_end = min(month_end, end_date)

            months.append((period_start, period_end))
            current = next_month

        return months

    @staticmethod
    def get_week_range(start_date: date, end_date: date) -> List[Tuple[date, date]]:
        """
        获取指定日期范围内的周范围列表

        Args:
            start_date: 开始日期
            end_date: 结束日期

        Returns:
            周范围列表，每个元素为(周一日期, 周日日期)
        """
        weeks = []

        # 找到开始日期所在周的周一
        days_since_monday = start_date.weekday()
        week_start = start_date - timedelta(days=days_since_monday)

        current = week_start

        while current <= end_date:
            week_end = current + timedelta(days=6)

            # 调整为实际范围
            period_start = max(current, start_date)
            period_end = min(week_end, end_date)

            if period_start <= period_end:
                weeks.append((period_start, period_end))

            current = week_end + timedelta(days=1)

        return weeks

    @staticmethod
    def get_workdays_in_range(start_date: date, end_date: date) -> int:
        """
        计算日期范围内的工作日天数（排除周末）

        Args:
            start_date: 开始日期
            end_date: 结束日期

        Returns:
            工作日天数
        """
        if start_date > end_date:
            return 0

        total_days = (end_date - start_date).days + 1
        full_weeks = total_days // 7
        remaining_days = total_days % 7

        workdays = full_weeks * 5

        # 处理剩余天数
        current = start_date + timedelta(days=full_weeks * 7)
        for _ in range(remaining_days):
            if current.weekday() < 5:  # 周一到周五
                workdays += 1
            current += timedelta(days=1)

        return workdays

    @staticmethod
    def is_workday(check_date: date) -> bool:
        """
        检查指定日期是否为工作日

        Args:
            check_date: 要检查的日期

        Returns:
            是否为工作日
        """
        return check_date.weekday() < 5  # 0-4为周一到周五

    @staticmethod
    def get_next_workday(from_date: date) -> date:
        """
        获取下一个工作日

        Args:
            from_date: 起始日期

        Returns:
            下一个工作日
        """
        next_day = from_date + timedelta(days=1)
        while not DateUtils.is_workday(next_day):
            next_day += timedelta(days=1)
        return next_day

    @staticmethod
    def get_previous_workday(from_date: date) -> date:
        """
        获取上一个工作日

        Args:
            from_date: 起始日期

        Returns:
            上一个工作日
        """
        prev_day = from_date - timedelta(days=1)
        while not DateUtils.is_workday(prev_day):
            prev_day -= timedelta(days=1)
        return prev_day

    @staticmethod
    def format_month_year(month_date: date, language: str = "jp") -> str:
        """
        格式化月份年份显示

        Args:
            month_date: 月份日期
            language: 语言设置

        Returns:
            格式化的月份年份字符串
        """
        if language == "jp":
            return f"{month_date.year}年{month_date.month}月"
        else:
            return month_date.strftime("%Y-%m")

    @staticmethod
    def parse_date_string(date_string: str) -> Optional[date]:
        """
        解析日期字符串

        Args:
            date_string: 日期字符串

        Returns:
            解析后的日期对象，失败返回None
        """
        date_formats = [
            "%Y-%m-%d",
            "%Y/%m/%d",
            "%Y年%m月%d日",
            "%Y.%m.%d",
            "%m/%d/%Y",
            "%d/%m/%Y"
        ]

        for fmt in date_formats:
            try:
                return datetime.strptime(date_string, fmt).date()
            except ValueError:
                continue

        return None

    @staticmethod
    def get_quarter_range(year: int, quarter: int) -> Tuple[date, date]:
        """
        获取指定年份和季度的日期范围

        Args:
            year: 年份
            quarter: 季度（1-4）

        Returns:
            季度范围(开始日期, 结束日期)

        Raises:
            ValueError: 季度值无效
        """
        if quarter not in [1, 2, 3, 4]:
            raise ValueError("季度值必须在1-4之间")

        start_month = (quarter - 1) * 3 + 1
        end_month = start_month + 2

        start_date = date(year, start_month, 1)
        end_date = date(year, end_month, calendar.monthrange(year, end_month)[1])

        return start_date, end_date

    @staticmethod
    def calculate_duration_in_units(start_date: date, end_date: date,
                                   unit: str = "days") -> float:
        """
        计算两个日期之间的时间差

        Args:
            start_date: 开始日期
            end_date: 结束日期
            unit: 时间单位 ("days", "weeks", "months", "years")

        Returns:
            时间差（浮点数）

        Raises:
            ValueError: 无效的时间单位
        """
        if start_date > end_date:
            return 0.0

        delta = end_date - start_date
        days = delta.days + 1  # 包含结束日期

        if unit == "days":
            return float(days)
        elif unit == "weeks":
            return days / 7.0
        elif unit == "months":
            # 近似计算，一个月按30天计算
            return days / 30.0
        elif unit == "years":
            return days / 365.0
        else:
            raise ValueError(f"不支持的时间单位：{unit}")

    @staticmethod
    def split_date_range_by_months(start_date: date, end_date: date,
                                  max_duration_months: int = 12) -> List[Tuple[date, date]]:
        """
        将长时间范围按月份拆分

        Args:
            start_date: 开始日期
            end_date: 结束日期
            max_duration_months: 最大持续月数

        Returns:
            拆分后的日期范围列表
        """
        ranges = []
        current_start = start_date

        while current_start <= end_date:
            # 计算当前段的结束日期
            current_end = current_start + relativedelta(months=max_duration_months) - timedelta(days=1)
            current_end = min(current_end, end_date)

            ranges.append((current_start, current_end))

            # 准备下一段
            current_start = current_end + timedelta(days=1)

        return ranges